// check integer
owl_byte owl_check_wchar_utf16(owl_wchar ch)
{
  // check params
  if (owl_check_wchar_unicode(ch) == 0
      || (ch >= OWL_INV_UTF16_RANGE1_MIN && ch <= OWL_INV_UTF16_RANGE1_MAX))
    return 0;
  
  // return the size
  if (ch >= OWL_UTF16_RANGE1_MIN && ch <= OWL_UTF16_RANGE2_MAX)
    return 2;
  /* ch >= OWL_UTF16_RANGE3_MIN && ch <= OWL_UTF16_RANGE3_MAX */
  return 4;
}

// check encoded
owl_byte owl_check_wchar_enc_utf16be(owl_byte * src, owl_umax size)
{
  // check params
  owl_byte ch_size = 0;
  if (owl_check_ptr(src, size) == owl_false)
    goto end;
 
  // check the starting bits of each byte of src
  // while checking if the characters can be read
  if (size >= 2 
      && (OWL_UTF16_MASK_1 & src[0]) != OWL_UTF16_BLOCK_1
      && (OWL_UTF16_MASK_1 & src[0]) != OWL_UTF16_BLOCK_2) // 2 bytes
    ch_size = 2;
  else if (size >= 4 
           && (OWL_UTF16_MASK_1 & src[0]) == OWL_UTF16_BLOCK_1
           && (OWL_UTF16_MASK_1 & src[2]) == OWL_UTF16_BLOCK_2) // 4 bytes
    ch_size = 4;
  // return
  end:
  return ch_size;
}

// check encoded
owl_byte owl_check_wchar_enc_utf16le(owl_byte * src, owl_umax size)
{
  // reuse owl_check_wchar_enc_utf16be
  // test for 2 bytes
  if (size < 2)
    return 0;
  owl_byte tmp[4] = {src[1], src[0]};
  if (owl_check_wchar_enc_utf16be(tmp, 2) != 0)
    return 2;
  // test for 4 bytes
  if (size < 4)
    return 0;
  tmp[2] = src[3];
  tmp[3] = src[2];
  return owl_check_wchar_enc_utf16be(tmp, 4);
}

// get integer
owl_wchar owl_get_wchar_utf16be(owl_byte * src, owl_umax size)
{
  // check params
  owl_byte ch_size = owl_check_wchar_enc_utf16be(src, size);
  if (ch_size == 0)
    return OWL_INV_UNICODE;
  
  // decode the character (EZPC)
  owl_wchar rtn = 0;
  if (ch_size == 2)
    rtn = (owl_wchar) ((src[0] << 8) + src[1]);
  else /* ch_size == 4 */
    rtn = (owl_wchar) ((~OWL_UTF16_MASK_1 & src[0]) << 18)
                       + (src[1] << 10)
                       + ((~OWL_UTF16_MASK_1 & src[2]) << 8)
                       + src[3]
                       + OWL_UTF16_LAST_RANGE_BIAS;
  return rtn;
}

// get integer
owl_wchar owl_get_wchar_utf16le(owl_byte * src, owl_umax size)
{
  // reuse owl_get_wchar_utf16be
  owl_byte ch_size = owl_check_wchar_enc_utf16le(src, size);
  if (ch_size == 0)
    return OWL_INV_UNICODE;
  
  owl_byte tmp[4] = {src[1], src[0]};
  // ch_size 2
  if (ch_size == 2)
    return owl_get_wchar_utf16be(tmp, 2);
  // ch_size 4
  tmp[2] = src[3];
  tmp[3] = src[2];
  return owl_get_wchar_utf16be(tmp, 4);
}

// write encoded
owl_byte * owl_write_wchar_enc_utf16be(owl_wchar ch, owl_byte * dest, owl_umax size)
{
  // check params
  owl_byte ch_size = owl_check_wchar_utf16(ch);
  if (ch_size == 0 || owl_check_ptr(dest, size) == owl_false || ch_size > size)
    return NULL;
  
  // encode the character (EZPC)
  if (ch_size == 2) {
    dest[1] = (owl_byte) (ch & 0x00FF);
    dest[0] = (owl_byte) ((ch & 0xFF00) >> 8);
  } else /* ch_size == 4 */ {
    ch -= OWL_UTF16_LAST_RANGE_BIAS;
    dest[3] = (owl_byte) (ch & 0x00FF);
    dest[2] = (owl_byte) ((ch & 0x0300) >> 8) + OWL_UTF16_BLOCK_2;
    dest[1] = (owl_byte) ((ch & (0x00FF << 10)) >> 10);
    dest[0] = (owl_byte) ((ch & (0x0300 << 10)) >> 18) + OWL_UTF16_BLOCK_1;
  }
  
  // return the last write position
  return dest + ch_size;
}

// write encoded
owl_byte * owl_write_wchar_enc_utf16le(owl_wchar ch, owl_byte * dest, owl_umax size)
{
  // reuse owl_write_wchar_enc_utf16be
  owl_byte ch_size = owl_check_wchar_utf16(ch),
           * rtn = owl_write_wchar_enc_utf16be(ch, dest, size); 
  if (rtn == NULL)
    return NULL;
  
  // invert the bytes according to its sizes
  owl_byte tmp = 0;
  // always invert these bytes
  tmp = dest[1];
  dest[1] = dest[0];
  dest[0] = tmp;
  if (ch_size == 4) {
    tmp = dest[3];
    dest[3] = dest[2];
    dest[2] = tmp;
  }
  
  // return the last write position
  return rtn;
}

// print integer
owl_byte owl_print_wchar_utf16(owl_wchar ch)
{
  if (owl_print_wchar_unicode(ch) == 0)
    return 0;
  return owl_check_wchar_utf16(ch);
}

// print encoded
owl_byte owl_print_wchar_enc_utf16be(owl_byte * src, owl_umax size)
{
  // check params
  owl_wchar ch = owl_get_wchar_utf16be(src, size);
  if (ch == OWL_INV_UNICODE) {
    printf(OWL_INV_ENC_UTF16BE_STR);
    return 0;
  }
  return owl_print_wchar_utf16(ch);
}

// print encoded
owl_byte owl_print_wchar_enc_utf16le(owl_byte * src, owl_umax size)
{
  // check params
  owl_wchar ch = owl_get_wchar_utf16le(src, size);
  if (ch == OWL_INV_UNICODE) {
    printf(OWL_INV_ENC_UTF16LE_STR);
    return 0;
  }
  return owl_print_wchar_utf16(ch);
}

// bruh
owl_wchar owl_wchar_utf16_as_unicode(owl_wchar utf16)
{
  if (owl_check_wchar_utf16(utf16) == 0)
    return OWL_INV_UNICODE;
  return utf16;
}

// hurb
owl_wchar owl_wchar_unicode_as_utf16(owl_wchar unicode)
{
  return owl_wchar_utf16_as_unicode(unicode);
}
